package main

import (
	"fmt"
	"net/http"
	"net/http/fcgi"
	"os/exec"
	"strconv"
	"strings"
)

type fastCgiHandler struct{}

func (h fastCgiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	// 获取 CGI 基础信息
	fastCgiEnv := fcgi.ProcessEnv(r)

	// 获取要执行的脚本
	scriptFile, ok := fastCgiEnv["SCRIPT_FILENAME"]
	if !ok {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	fastCgiEnv["REQUEST_URI"] = r.URL.String()
	fastCgiEnv["REQUEST_METHOD"] = r.Method
	fastCgiEnv["CONTENT_LENGTH"] = r.Header.Get("Content-Length")
	fastCgiEnv["CONTENT_TYPE"] = r.Header.Get("Content-Type")

	// 获取必要的 HTTP 请求参数
	needHeaders := []string{
		"Content-Type", "Accept", "Accept-Encoding",
		"Authorization", "Connection", "Referer",
		"User-Agent", "Cookie",
	}
	for _, v := range needHeaders {
		tmp := r.Header.Get(v)
		if tmp != "" {
			fastCgiEnv["HTTP_"+strings.ToUpper(v)] = tmp
		}
	}

	// 获取环境变量
	phpExecEnv := make([]string, 0)
	for k, v := range fastCgiEnv {
		phpExecEnv = append(phpExecEnv, fmt.Sprintf("%s=%s", k, v))
	}

	// 通过CGI程序执行
	phpProcess := &exec.Cmd{
		Env:   phpExecEnv,
		Path:  ExecFile,
		Args:  []string{ExecFile, scriptFile},
		Stdin: r.Body,
	}

	// 处理执行结果
	phpExecResultBytes, err := phpProcess.Output()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	phpExecResult := string(phpExecResultBytes)

	// 处理 CGI 程序返回的 Header
	headerEnd := strings.Index(phpExecResult, "\r\n\r\n")
	if headerEnd >= 0 {
		statusCode := 200
		contentBody := phpExecResultBytes[(headerEnd + 4):]
		contentHeader := string(phpExecResultBytes[:headerEnd])
		contentHeaderLines := strings.Split(contentHeader, "\r\n")
		for _, v := range contentHeaderLines {

			headerKeyEnd := strings.Index(v, ":")
			headerKey := strings.TrimSpace(v[:headerKeyEnd])
			headerValue := v[(headerKeyEnd + 1):]

			if headerKey != "" {

				// 处理 PHP 端设置的 http 状态码
				if headerKey == "Status" {
					statusSups := strings.Split(strings.TrimSpace(headerValue), " ")
					if len(statusSups) >= 1 {
						statusCode, err = strconv.Atoi(statusSups[0])
					}
				}else{
					w.Header().Set(strings.TrimSpace(headerKey), strings.TrimSpace(headerValue))
				}
			}
		}
		w.Write(contentBody)
		w.WriteHeader(statusCode)
	} else {
		w.Write(phpExecResultBytes)
	}
}
